package org.dse.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.LockMode;
import org.hibernate.criterion.Criterion;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

/**
 * This class gives implementation on DAO operations, using Hibernate
 * technology. It implements all the operations like findById, findAll, save,
 * delete and so on.
 * 
 * The class also use generic type.
 * 
 * @author zhangchen
 * 
 */
public abstract class GenericHibernateDAO<T, ID extends Serializable> extends
		HibernateDaoSupport implements GenericDAO<T, ID> {

	private Class<T> persistentClass;

	@SuppressWarnings("unchecked")
	public GenericHibernateDAO() {
		this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
	}

	public Class<T> getPersistentClass() {
		return persistentClass;
	}

	/**
	 * Use Hibernate get method to query an object using its primary key.
	 * 
	 * @param id
	 *            the primary key
	 * @param lock
	 *            the lock mode
	 * @return the object identified by the primary key
	 */
	@SuppressWarnings("unchecked")
	public T findById(ID id, boolean lock) {
		T entity;
		if (lock)
			entity = (T) getHibernateTemplate().get(getPersistentClass(), id,
					LockMode.UPGRADE);
		else
			entity = (T) getHibernateTemplate().get(getPersistentClass(), id);

		return entity;
	}

	/**
	 * Query all objects in a specific table.
	 * 
	 * @return a list containing all objects
	 */
	@SuppressWarnings("unchecked")
	public List<T> findAll() {
		return getHibernateTemplate().loadAll(getPersistentClass());
	}

	/**
	 * Query some objects like another object
	 * 
	 * @param exampleInstance
	 *            the object to be treated as the template
	 * @return a list containing proper objects
	 */
	@SuppressWarnings("unchecked")
	public List<T> findByExample(T exampleInstance) {
		return getHibernateTemplate().findByExample(exampleInstance);
	}

	/**
	 * Use Hibernate saveOrUpdate method to save an object.
	 * 
	 * @param entity
	 *            the object to be saved
	 * @return the same object
	 */
	public T makePersistent(T entity) {
		getHibernateTemplate().saveOrUpdate(entity);
		return entity;
	}

	/**
	 * Use Hibernate delete method to delete an object from the database.
	 * 
	 * @param entity
	 *            the object to be deleted
	 */
	public void makeTransient(T entity) {
		getHibernateTemplate().delete(entity);
	}

	/**
	 * Flush the cache in Hibernate.
	 */
	public void flush() {
		getHibernateTemplate().flush();
	}

	/**
	 * Clear the cache in Hibernate.
	 */
	public void clear() {
		getHibernateTemplate().clear();
	}

	/**
	 * Use this inside subclasses as a convenience method.
	 */
	@SuppressWarnings("unchecked")
	protected List<T> findByCriteria(Criterion... criterion) {
		Criteria crit = getSession().createCriteria(getPersistentClass());
		for (Criterion c : criterion) {
			crit.add(c);
		}
		return crit.list();
	}
}
